package solver;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import model.Problem;
import model.Process;
import model.Solution;

public class MathSolver extends BaseSolver {
	private final static int TIME_LIMIT_SEC = 200;
	private int numberOfSelectedProcesses;
	private int numberOfEligibleMachinesPerProcess;

	public MathSolver(Problem problem, Solution initial_solution) {
		super(problem, initial_solution);
		numberOfSelectedProcesses = 10;
		numberOfEligibleMachinesPerProcess = 10;
	}

	public MathSolver(Problem problem, Solution initial_solution,
			int numberOfSelectedProcesses,
			int numberOfEligibleMachinesPerProcess) {
		super(problem, initial_solution);
		this.numberOfSelectedProcesses = numberOfSelectedProcesses;
		this.numberOfEligibleMachinesPerProcess = numberOfEligibleMachinesPerProcess;
	}

	public void solve() {
		GrbExtraLeanIPModelBuilder grbExtraLeanIPModelBuilder;
		setCurrentSolution(getInitialSolution().copy());
		getFullValidator().setSolutionPair(getInitialSolution(), getCurrentSolution());
		getFullValidator().computeCost();
		long best_cost = getFullValidator().getInitialSolutionCost();
		int improvement_counter = 0;
		for (int i = 0; i < 100; i++) {
			System.out
					.printf("######################################  ROUND %d ######################################\n",
							i + 1);
			getFullValidator().printInitialSolutionCostAnalysis();
			getFullValidator().printNewSolutionCostAnalysis("CUR Solution");
			List<Integer> selectedProcesses = randomlySelectProcessesAndMachines(
					numberOfSelectedProcesses,
					numberOfEligibleMachinesPerProcess);

			grbExtraLeanIPModelBuilder = new GrbExtraLeanIPModelBuilder(
					env.getProblem(), selectedProcesses, true);
			grbExtraLeanIPModelBuilder.setInitialSolution(getInitialSolution());
			grbExtraLeanIPModelBuilder.setCurrentSolution(getCurrentSolution());
			// grbExtraLeanIPModelBuilder.includeTransientHardConstraints(true);
			for (Integer p_id : selectedProcesses) {
				Process process = getProblem().getProcesses().get(p_id);
				System.out.println(process.getEligibleMachinesInfo());
			}
			boolean saveLP = false;
			boolean solved = grbExtraLeanIPModelBuilder.solve(saveLP);
			if (solved) {
				setPotentialSolution(grbExtraLeanIPModelBuilder
						.saveToSolution());
				getFullValidator().setSolutionPair(getInitialSolution(), getPotentialSolution());
				getFullValidator().computeCost();
				long potential_solution_cost = getFullValidator().getNewSolutionCost();
				if (potential_solution_cost < best_cost) {
					improvement_counter++;
					System.out
							.printf("Better solution found (round=%d, improvement=%d) %,d (reduced by %,d)\n",
									i + 1, improvement_counter,
									potential_solution_cost, best_cost
											- potential_solution_cost);
					getFullValidator().printNewSolutionCostAnalysis("POT Solution");
					if (getFullValidator().isFeasible()) {
						System.out.println(":)");
					} else {
						System.out.println(":(");
						GrbExtraLeanIPModelBuidlerAnalyzer analyzer = new GrbExtraLeanIPModelBuidlerAnalyzer(
								getProblem(), getInitialSolution(), getCurrentSolution(),
								selectedProcesses);
						analyzer.analyze();
						return;
					}
					best_cost = potential_solution_cost;
					setCurrentSolution(getPotentialSolution().copy());
				} else {
					System.out.printf(
							"No better solution found was %,d  is %,d\n",
							best_cost, potential_solution_cost);
					getFullValidator().printNewSolutionCostAnalysis("POT Solution");
				}
				if (getFullValidator().isFeasible()) {
					System.out.println(":)");
				} else {
					System.out.println(":(");
					GrbExtraLeanIPModelBuidlerAnalyzer analyzer = new GrbExtraLeanIPModelBuidlerAnalyzer(
							getProblem(), getInitialSolution(), getCurrentSolution(),
							selectedProcesses);
					analyzer.analyze();
					return;
				}
			} else {
				GrbExtraLeanIPModelBuidlerAnalyzer analyzer = new GrbExtraLeanIPModelBuidlerAnalyzer(
						getProblem(), getInitialSolution(), getCurrentSolution(),
						selectedProcesses);
				analyzer.analyze();
				return;
			}
		}
	}

	private List<Integer> randomlySelectProcessesAndMachines(int nop, int noem) {
		getProblem().clearEligibleMachines();
		nop = nop < getProblem().P ? nop : getProblem().P;
		noem = noem < getProblem().M ? noem : getProblem().M;
		List<Integer> selectedProcesses = new ArrayList<Integer>();
		List<Integer> ballot = new ArrayList<Integer>();
		for (int p_id = 0; p_id < getProblem().P; p_id++) {
			ballot.add(p_id);
		}
		Collections.shuffle(ballot, getRandom());
		for (int i = 0; i < nop; i++)
			selectedProcesses.add(ballot.get(i));
		for (Integer p_id : selectedProcesses) {
			Process proc = getProblem().getProcesses().get(p_id);
			ballot = new ArrayList<Integer>();
			for (int m_id = 0; m_id < getProblem().M; m_id++) {
				ballot.add(m_id);
			}
			Collections.shuffle(ballot, getRandom());
			int current_machine_id = getCurrentSolution()
					.getMachineIdForProcessId(p_id);
			proc.addEligibleMachine(current_machine_id);
			ballot.remove(new Integer(current_machine_id));
			for (int i = 0; i < noem - 1; i++) {
				proc.addEligibleMachine(ballot.get(i));
			}
		}
		return selectedProcesses;
	}

	@Override
	public int solveWithTimeLimit(int seconds) {
		//TODO fix this
		solve();
		return 0;
	}

	
	
}
